#include "PluginManager.h"
#include <QObject>
#include <QtScript/QtScript>
#include "StarlabException.h"

#if defined(Q_OS_WIN)
    QString LIB_EXTENSION_FILTER("*.dll");
#elif defined(Q_OS_MAC)
    QString LIB_EXTENSION_FILTER("*.dylib");
#endif

#ifdef DEBUG_PLUGINMANAGER
    #define DBG
#else
    #define DBG if(0)
#endif
    
QString failurecauses_qtplugin(
"\nPOSSIBLE FAILURE REASONS:\n"
"  1) plugin needs a DLL which cannot be found in the executable folder\n"
"  2) ... add your reason?");
QString failurecauses_starlabplugin(
"\nPOSSIBLE FAILURE REASONS:\n"
"  2) starlab PluginManager does not know how to load it\n"
"  3) ... add your reason?");

PluginManager::PluginManager(){
    //pluginsDir=QDir(getPluginDirPath()); 
    // without adding the correct library path in the mac the loading of jpg (done via qt plugins) fails
    //qApp->addLibraryPath(getPluginDirPath());
    //qApp->addLibraryPath(getBaseDirPath());
    
    try{
        loadPlugins();
    } catch ( StarlabException& e ) {
        qDebug() << e.what();
    } 
}

void PluginManager::loadPlugins() {
    DEBUGLINE qDebug() << "[[ENTERING]] PluginManager::loadPlugins(..)";
    
    pluginsDir=QDir(getPluginDirPath());
    // without adding the correct library path in the mac the loading of jpg (done via qt plugins) fails
    qApp->addLibraryPath(getPluginDirPath());
    qApp->addLibraryPath(getBaseDirPath());
    QStringList pluginfilters(LIB_EXTENSION_FILTER);
    //only the file with extension pluginfilters will be listed by function entryList()
    pluginsDir.setNameFilters(pluginfilters);

    qDebug( "Loading plugins from: %s ",qPrintable(pluginsDir.absolutePath()));
    
    /// Load all plugins
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
        
        /// Attempt to load a Qt plugin
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject* plugin = loader.instance();
        if(!plugin) qDebug() << "Plugin load failure: " << fileName;
        if(!plugin) throw StarlabException("Plugin " + fileName + "is not a proper *Qt* plugin!! " + failurecauses_qtplugin);
        DBG qDebug() << "Loading plugin from file: " << fileName;
        
        /// Attempt to load one of the starlab plugins
        bool loadok = false;
        loadok |= load_InputOutputPlugin(plugin);
        loadok |= load_ProjectInputOutputPlugin(plugin);
        loadok |= load_FilterPlugin(plugin);       
        loadok |= load_DecoratePlugin(plugin);
        loadok |= load_GuiPlugin(plugin);        
        loadok |= load_EditPlugin(plugin);
        loadok |= load_DrawAreaPlugin(plugin);
        if( !loadok ) 
            throw StarlabException("plugin "+fileName+" was not recognized as one of the declared Starlab plugin!!"); // +failurecauses_starlabplugin));
                
        /// If we read here loading went ok
        DBG qDebug() << "Plugin: " << fileName << " loaded succesfully";
    }
}

#ifdef OLDMESHLAB
/*
This function create a map from filtername to dummy RichParameterSet.
containing for each filtername the set of parameter that it uses.
*/
QMap<QString, RichParameterSet> PluginManager::generateFilterParameterMap() {
    QMap<QString,RichParameterSet> FPM;
/// @todo useful to show parameters to command line parser
    MeshDocument md;
    MeshModel* mm = md.addNewMesh("","dummy",true);
    vcg::tri::Tetrahedron<CMeshO>(mm->cm);
    mm->updateDataMask(MeshModel::MM_ALL);
    
    foreach(MeshFilterInterface* iFilter,stringFilterMap) {
        QString filterName = iFilter->name();
        RichParameterSet rp;
        stringFilterMap[filterName]->initParameterSet(md,rp);
        FPM[filterName]=rp;
    }
    return FPM;
}
#endif

QString PluginManager::getBaseDirPath() {
    QDir baseDir(qApp->applicationDirPath());

#if defined(Q_OS_WIN)
    // Windows:
    // during development with visual studio binary could be in the debug/release subdir.
    // once deployed plugins dir is in the application directory, so
    if (baseDir.dirName() == "debug" || baseDir.dirName() == "release")		
        baseDir.cdUp();
#endif

#if defined(Q_OS_MAC)
    // Mac: during developmentwith xcode  and well deployed the binary is well buried.
    for(int i=0; i<6; ++i) {
        if(baseDir.exists("plugins")) break;
        baseDir.cdUp();
    }
    // qDebug([[DEBUG]] The base dir is %s",qPrintable(baseDir.absolutePath()));
#endif
    return baseDir.absolutePath();
}

QString PluginManager::getPluginDirPath() {
    QDir pluginsDir(getBaseDirPath());
    if(!pluginsDir.exists("plugins"))
        //QMessageBox::warning(0,"Meshlab Initialization","Serious error. Unable to find the plugins directory.");
        qDebug("Meshlab Initialization: Serious error. Unable to find the plugins directory.");
    pluginsDir.cd("plugins");
    return pluginsDir.absolutePath();
}

bool PluginManager::load_ProjectInputOutputPlugin(QObject *plugin){
    ProjectInputOutputPlugin* iIO = qobject_cast<ProjectInputOutputPlugin*>(plugin);
    if(!iIO) return false;
    projectIOPlugins.push_back(iIO);
    QString ext = iIO->extension().toLower();
    projectExtensionToPlugin.insert(ext,iIO);
    return true;
}

#include <QRegExp>
bool PluginManager::load_InputOutputPlugin(QObject *plugin){
    InputOutputPlugin* iIO = qobject_cast<InputOutputPlugin*>(plugin);
    if(!iIO) return false;
    modelIOPlugins.insert(iIO->name(), iIO);
    /// Parse the extension filter into extensions
    {
        QString extensionFilter = iIO->name();
        DEB qDebug() << "Parsing extension filter: " << extensionFilter;
        int i_open  = extensionFilter.indexOf("(");
        int i_close = extensionFilter.indexOf(")");
        // Chunk of string between parenthesis, then split by comma
        extensionFilter = extensionFilter.mid(i_open+1,i_close-i_open-1);
        QStringList extensions = extensionFilter.split(QRegExp(","));
        foreach(QString extension, extensions){
            int i_dot = extension.indexOf(".");
            extension = extension.mid(i_dot+1);
            DEB qDebug() << "extracted extension: " << extension;
            modelExtensionToPlugin.insert(extension.toLower(),iIO);
        }
    }
    return true;
}

bool PluginManager::load_FilterPlugin(QObject *plugin){
    FilterPlugin* iFilter = qobject_cast<FilterPlugin*>(plugin);
    if(!iFilter) return false;
    filterPlugins.insert(iFilter->name(), iFilter);
    return true;
}

bool PluginManager::load_DecoratePlugin(QObject *plugin){
    DecoratePlugin* iDecorate = qobject_cast<DecoratePlugin*>(plugin);
    if(!iDecorate) return false;
    decoratePlugins.insert(iDecorate->name(), iDecorate);
    return true;
}
bool PluginManager::load_GuiPlugin(QObject* _plugin){
    GuiPlugin* plugin = qobject_cast<GuiPlugin*>(_plugin);
    if(!plugin) return false;
    guiPlugins.insert(plugin->name(), plugin);
    return true;
}

bool PluginManager::load_EditPlugin(QObject* _plugin){
    EditPlugin* plugin = qobject_cast<EditPlugin*>(_plugin);
    if(!plugin) return false;
    editPlugins.insert(plugin->name(), plugin);    
    return true;
}
 
bool PluginManager::load_DrawAreaPlugin(QObject *_plugin){
    DrawAreaPlugin* plugin = qobject_cast<DrawAreaPlugin*>(_plugin);
    if(!plugin) return false;
    drawAreaPlugins.insert(plugin->name(), plugin);    
    return true;
} 

QString PluginManager::getFilterStrings(){
    QStringList filters;
    
    /// Fill in filters for Model files
    /// @todo add the readable format name
    QStringList extensions= modelExtensionToPlugin.keys();
    foreach(QString extension, extensions)
        filters.append("*."+extension);
    
    /// Fill in filters for Document files
    filters.prepend("Starlab Project (*.starlab)");
    filters.prepend("All Files (*.*)");
    
    /// Merge in a ;; separated list
    return filters.join(";;");
}



